﻿using System;
using System.Drawing;

using Storm.TextEditor.Editor;
using Storm.TextEditor.Editor.Text;

namespace Storm.TextEditor.Parser.Objects.Collections
{
    /// <summary>
    /// Represents a collection of FormatRange.
    /// </summary>
    public class FormatRangeCollection
        : BaseCollection
    {
        #region Fields

        private SyntaxDocument document = null;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the document of the FormatRangeCollection.
        /// </summary>
        internal SyntaxDocument Document
        {
            get { return document; }
        }

        public FormatRange this[int index]
        {
            get { return List[index] as FormatRange; }
        }

        #endregion

        #region Methods

        #region Public

        /// <summary>
        /// Adds the given FormatRange to the FormatRangeCollection.
        /// </summary>
        /// <param name="item">Item to add.</param>
        /// <returns>Index of the added item.</returns>
        public int Add(FormatRange item)
        {
            item.Document = this.Document;
            item.Apply();

            return this.List.Add(item);
        }

        /// <summary>
        /// Removes the given FormatRange from the FormatRangeCollection.
        /// </summary>
        /// <param name="item">Item to remove.</param>
        public void Remove(FormatRange item)
        {
            this.List.Remove(item);
        }

        /// <summary>
        /// Returns whether the given Row contains the FormatRanges of the FormatRangeCollection.
        /// </summary>
        /// <param name="row">Row to check whether contains the FormatRanges of the FormatRangeCollection.</param>
        /// <returns>Whether the given Row contains the FormatRanges of the FormatRangeCollection.</returns>
        public bool RowContainsFormats(Row row)
        {
            foreach (FormatRange formatRange in this)
            {
                if (formatRange.Bounds.FirstRow <= row.Index && formatRange.Bounds.LastRow >= row.Index)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Merges the given coordinates into the FormatRanges of the FormatRangeCollection.
        /// </summary>
        /// <param name="x">X coordinate to merge.</param>
        /// <param name="y">Y coordinate to merge.</param>
        /// <returns>The new FormatRange.</returns>
        public FormatRange MergeFormatRanges(int x, int y)
        {
            FormatRange formatRange = null;
            foreach (FormatRange exFormatRange in this)
            {
                if (exFormatRange.Contains(x, y) != 0)
                    continue;

                if (exFormatRange.BackColor != Color.Empty)
                {
                    if (formatRange == null)
                        formatRange = new FormatRange();

                    formatRange.BackColor = exFormatRange.BackColor;
                }

                if (exFormatRange.ForeColor != Color.Empty)
                {
                    if (formatRange == null)
                        formatRange = new FormatRange();

                    formatRange.ForeColor = exFormatRange.ForeColor;
                }

                if (exFormatRange.WaveColor != Color.Empty)
                {
                    if (formatRange == null)
                        formatRange = new FormatRange();

                    formatRange.WaveColor = exFormatRange.WaveColor;
                }
            }

            return formatRange;
        }

        /// <summary>
        /// Expands the FormatRange at the given coordinates by the given text.
        /// </summary>
        /// <param name="x">X coordinate of the FormatRange.</param>
        /// <param name="y">Y coordinate of the FormatRange.</param>
        /// <param name="text">Text to expand with.</param>
        public void Expand(int x, int y, string text)
        {
            if (this.RowContainsFormats(this.Document[y]) == true)
            {
                string newText = text.Replace(Environment.NewLine, "\n");
                int length = newText.Length;

                string[] lines = newText.Split('\n');
                foreach (FormatRange formatRange in this)
                {
                    int res = formatRange.Contains(x, y);
                    if (res == 0)
                    {
                        formatRange.Bounds.LastRow += lines.Length - 1;
                        if (formatRange.Bounds.LastRow == y + lines.Length - 1)
                            formatRange.Bounds.LastColumn += lines[lines.Length - 1].Length;
                    }
                    else if (res == -1)
                    {
                        formatRange.Bounds.FirstRow += lines.Length - 1;
                        formatRange.Bounds.LastRow += lines.Length - 1;
                        if (formatRange.Bounds.FirstRow == y + lines.Length - 1)
                        {
                            if (lines.Length > 1)
                                formatRange.Bounds.FirstColumn -= x;

                            formatRange.Bounds.FirstColumn += lines[lines.Length - 1].Length;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Shrinks the FormatRange at the given TextRange's position by the TextRange's bounds.
        /// </summary>
        /// <param name="textRange">TextRange to find FormatRange at.</param>
        public void Shrink(TextRange textRange)
        {
            string text = Document.GetRange(textRange);
            string newText = text.Replace(Environment.NewLine, "\n");
            int length = newText.Length;
            string[] lines = newText.Split('\n');

            foreach (FormatRange formatRange in this)
            {
                int res = formatRange.Contains(textRange.FirstColumn, textRange.FirstRow);
                int res2 = formatRange.Contains(textRange.LastColumn, textRange.LastRow);

                int rows = textRange.LastRow - textRange.FirstRow;
                if (res == -1 && res2 == -1)
                {
                    formatRange.Bounds.FirstRow -= rows;
                    formatRange.Bounds.LastRow -= rows;
                    if (formatRange.Bounds.FirstRow == textRange.FirstRow + lines.Length - 1)
                    {
                        if (lines.Length > 1)
                            formatRange.Bounds.FirstColumn -= textRange.FirstColumn;

                        formatRange.Bounds.FirstColumn -= lines[lines.Length - 1].Length;
                    }
                    else if (formatRange.Bounds.FirstRow == textRange.FirstRow)
                    {
                        if (lines.Length > 1)
                            formatRange.Bounds.FirstColumn += textRange.FirstColumn;

                        formatRange.Bounds.FirstColumn -= lines[lines.Length - 1].Length;
                    }
                }
                else if (res == -1 && res2 == 0)
                {
                    formatRange.Bounds.FirstRow -= rows;
                    formatRange.Bounds.LastRow -= rows;
                    formatRange.Bounds.FirstColumn = textRange.FirstColumn;
                }
                else if (res == 0 && res2 == 0)
                {
                    formatRange.Bounds.LastRow -= rows;
                    if (formatRange.Bounds.LastRow == textRange.FirstRow)
                    {
                        if (rows == 0)
                            formatRange.Bounds.LastColumn -= lines[lines.Length - 1].Length;
                        else
                            formatRange.Bounds.LastColumn = textRange.FirstColumn + formatRange.Bounds.LastColumn;
                    }
                }
                else if (res == 0 && res2 == 1)
                {
                    formatRange.Bounds.LastColumn = textRange.FirstColumn - 1;
                    formatRange.Bounds.LastRow = textRange.FirstRow;
                }

                if (res == -1 && res2 == 1)
                    formatRange.Bounds.SetBounds(-1, -1, -1, -1);
            }
        }

        #endregion

        #endregion

        /// <summary>
        /// Initializes a new instance of FormatRangeCollection.
        /// </summary>
        public FormatRangeCollection(SyntaxDocument document)
        {
            this.document = document;
        }
    }
}
